package com.kexio.dynamic.permission.rule;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 权限规则评估器
 * 
 * 负责执行权限规则并返回评估结果
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Component
public class RuleEvaluator {

    private static final Logger log = LoggerFactory.getLogger(RuleEvaluator.class);

    private final RuleParser ruleParser;

    public RuleEvaluator(RuleParser ruleParser) {
        this.ruleParser = ruleParser;
    }

    /**
     * 评估权限规则
     * 
     * @param rule    权限规则
     * @param context 评估上下文
     * @return 评估结果
     */
    public EvaluationResult evaluate(PermissionRule rule, Map<String, Object> context) {
        if (rule == null) {
            return EvaluationResult.error("权限规则为空");
        }

        if (!rule.isEffective()) {
            return EvaluationResult.error("权限规则未生效或已失效");
        }

        long startTime = System.currentTimeMillis();
        
        try {
            boolean result = evaluateByType(rule, context);
            long duration = System.currentTimeMillis() - startTime;
            
            log.debug("权限规则评估完成: 规则={}, 结果={}, 耗时={}ms", 
                     rule.getRuleCode(), result, duration);
            
            return EvaluationResult.success(result, duration);
            
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("权限规则评估失败: 规则={}, 错误={}", rule.getRuleCode(), e.getMessage(), e);
            
            return EvaluationResult.error("规则评估失败: " + e.getMessage(), duration);
        }
    }

    /**
     * 批量评估权限规则
     * 
     * @param rules   权限规则列表
     * @param context 评估上下文
     * @return 批量评估结果
     */
    public BatchEvaluationResult batchEvaluate(java.util.List<PermissionRule> rules, Map<String, Object> context) {
        BatchEvaluationResult batchResult = new BatchEvaluationResult();
        
        if (rules == null || rules.isEmpty()) {
            batchResult.setSuccess(true);
            batchResult.setResult(true); // 没有规则时默认允许
            return batchResult;
        }

        long startTime = System.currentTimeMillis();
        
        for (PermissionRule rule : rules) {
            EvaluationResult result = evaluate(rule, context);
            batchResult.addResult(rule.getRuleCode(), result);
            
            // 如果是必需规则且失败，则整体失败
            if (!result.isSuccess() || !result.getResult()) {
                batchResult.setSuccess(false);
                batchResult.setResult(false);
                batchResult.setFailureReason("规则验证失败: " + rule.getRuleCode());
                break;
            }
        }
        
        if (batchResult.isSuccess()) {
            batchResult.setResult(true);
        }
        
        batchResult.setTotalDuration(System.currentTimeMillis() - startTime);
        return batchResult;
    }

    /**
     * 根据规则类型进行评估
     */
    private boolean evaluateByType(PermissionRule rule, Map<String, Object> context) {
        switch (rule.getRuleType()) {
            case SPEL:
                return evaluateSpelExpression(rule.getRuleExpression(), context);
            case GROOVY:
                return evaluateGroovyScript(rule.getRuleExpression(), context);
            case JAVASCRIPT:
                return evaluateJavaScript(rule.getRuleExpression(), context);
            case SIMPLE:
                return evaluateSimpleCondition(rule.getRuleExpression(), context);
            case COMPOSITE:
                return evaluateCompositeRule(rule, context);
            default:
                throw new UnsupportedOperationException("不支持的规则类型: " + rule.getRuleType());
        }
    }

    /**
     * 评估SpEL表达式
     */
    private boolean evaluateSpelExpression(String expression, Map<String, Object> context) {
        Expression expr = ruleParser.parseSpelExpression(expression);
        EvaluationContext evalContext = createSpelContext(context);
        
        Object result = expr.getValue(evalContext);
        return convertToBoolean(result);
    }

    /**
     * 评估Groovy脚本
     */
    private boolean evaluateGroovyScript(String script, Map<String, Object> context) {
        try {
            groovy.lang.Script groovyScript = (groovy.lang.Script) ruleParser.parseGroovyScript(script);
            
            // 设置上下文变量
            if (context != null) {
                for (Map.Entry<String, Object> entry : context.entrySet()) {
                    groovyScript.setProperty(entry.getKey(), entry.getValue());
                }
            }
            
            // 添加常用变量
            groovyScript.setProperty("now", System.currentTimeMillis());
            groovyScript.setProperty("today", java.time.LocalDate.now());
            groovyScript.setProperty("time", java.time.LocalTime.now());
            
            Object result = groovyScript.run();
            return convertToBoolean(result);
            
        } catch (Exception e) {
            log.error("Groovy脚本执行失败: script={}, error={}", script, e.getMessage(), e);
            throw new RuntimeException("Groovy脚本执行失败: " + e.getMessage(), e);
        }
    }

    /**
     * 评估JavaScript脚本
     */
    private boolean evaluateJavaScript(String script, Map<String, Object> context) {
        try {
            javax.script.CompiledScript compiledScript = (javax.script.CompiledScript) ruleParser.parseJavaScript(script);
            
            // 创建执行上下文
            javax.script.SimpleBindings bindings = new javax.script.SimpleBindings();
            
            // 设置上下文变量
            if (context != null) {
                bindings.putAll(context);
            }
            
            // 添加常用变量
            bindings.put("now", System.currentTimeMillis());
            bindings.put("today", java.time.LocalDate.now().toString());
            bindings.put("time", java.time.LocalTime.now().toString());
            
            Object result = compiledScript.eval(bindings);
            return convertToBoolean(result);
            
        } catch (Exception e) {
            log.error("JavaScript脚本执行失败: script={}, error={}", script, e.getMessage(), e);
            throw new RuntimeException("JavaScript脚本执行失败: " + e.getMessage(), e);
        }
    }

    /**
     * 评估简单条件
     */
    private boolean evaluateSimpleCondition(String condition, Map<String, Object> context) {
        RuleParser.SimpleCondition simpleCondition = ruleParser.parseSimpleCondition(condition);
        
        String field = simpleCondition.getField();
        String operator = simpleCondition.getOperator();
        Object expectedValue = simpleCondition.getValue();
        
        // 从上下文获取字段值
        Object actualValue = getFieldValue(field, context);
        
        return evaluateCondition(actualValue, operator, expectedValue);
    }
    
    /**
     * 从上下文获取字段值
     */
    private Object getFieldValue(String field, Map<String, Object> context) {
        if (context == null) {
            return null;
        }
        
        // 支持嵌套字段访问，如 user.age
        if (field.contains(".")) {
            String[] parts = field.split("\\.");
            Object current = context.get(parts[0]);
            
            for (int i = 1; i < parts.length && current != null; i++) {
                if (current instanceof Map) {
                    current = ((Map<?, ?>) current).get(parts[i]);
                } else {
                    // 使用反射获取属性
                    try {
                        java.lang.reflect.Field reflectField = current.getClass().getDeclaredField(parts[i]);
                        reflectField.setAccessible(true);
                        current = reflectField.get(current);
                    } catch (Exception e) {
                        return null;
                    }
                }
            }
            
            return current;
        }
        
        return context.get(field);
    }
    
    /**
     * 评估条件
     */
    private boolean evaluateCondition(Object actualValue, String operator, Object expectedValue) {
        switch (operator) {
            case "==":
                return java.util.Objects.equals(actualValue, expectedValue);
            case "!=":
                return !java.util.Objects.equals(actualValue, expectedValue);
            case ">":
                return compareValues(actualValue, expectedValue) > 0;
            case ">=":
                return compareValues(actualValue, expectedValue) >= 0;
            case "<":
                return compareValues(actualValue, expectedValue) < 0;
            case "<=":
                return compareValues(actualValue, expectedValue) <= 0;
            case "contains":
                return actualValue != null && actualValue.toString().contains(expectedValue.toString());
            case "startsWith":
                return actualValue != null && actualValue.toString().startsWith(expectedValue.toString());
            case "endsWith":
                return actualValue != null && actualValue.toString().endsWith(expectedValue.toString());
            case "matches":
                return actualValue != null && actualValue.toString().matches(expectedValue.toString());
            default:
                throw new UnsupportedOperationException("不支持的操作符: " + operator);
        }
    }
    
    /**
     * 比较值大小
     */
    @SuppressWarnings("unchecked")
    private int compareValues(Object actual, Object expected) {
        if (actual == null || expected == null) {
            return actual == expected ? 0 : (actual == null ? -1 : 1);
        }
        
        if (actual instanceof Number && expected instanceof Number) {
            return Double.compare(((Number) actual).doubleValue(), ((Number) expected).doubleValue());
        }
        
        if (actual instanceof Comparable && expected instanceof Comparable) {
            return ((Comparable<Object>) actual).compareTo(expected);
        }
        
        return actual.toString().compareTo(expected.toString());
    }

    /**
     * 评估复合规则
     */
    private boolean evaluateCompositeRule(PermissionRule rule, Map<String, Object> context) {
        try {
            String expression = rule.getRuleExpression();
            
            // 解析JSON格式的复合规则
            com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
            CompositeRuleExpression compositeExpression = objectMapper.readValue(expression, CompositeRuleExpression.class);
            
            return evaluateCompositeExpression(compositeExpression, context);
            
        } catch (Exception e) {
            log.error("复合规则评估失败: expression={}, error={}", rule.getRuleExpression(), e.getMessage(), e);
            // 如果解析失败，尝试作为SpEL表达式执行
            return evaluateSpelExpression(rule.getRuleExpression(), context);
        }
    }
    
    /**
     * 评估复合表达式
     */
    private boolean evaluateCompositeExpression(CompositeRuleExpression expression, Map<String, Object> context) {
        String operator = expression.getOperator();
        java.util.List<Object> operands = expression.getOperands();
        
        if (operands == null || operands.isEmpty()) {
            return true;
        }
        
        switch (operator.toUpperCase()) {
            case "AND":
                return operands.stream().allMatch(operand -> evaluateOperand(operand, context));
            case "OR":
                return operands.stream().anyMatch(operand -> evaluateOperand(operand, context));
            case "NOT":
                if (operands.size() != 1) {
                    throw new IllegalArgumentException("NOT操作符只能有一个操作数");
                }
                return !evaluateOperand(operands.get(0), context);
            default:
                throw new UnsupportedOperationException("不支持的复合操作符: " + operator);
        }
    }
    
    /**
     * 评估操作数
     */
    private boolean evaluateOperand(Object operand, Map<String, Object> context) {
        if (operand instanceof Boolean) {
            return (Boolean) operand;
        }
        
        if (operand instanceof String) {
            // 作为简单条件评估
            return evaluateSimpleCondition((String) operand, context);
        }
        
        if (operand instanceof Map) {
            // 嵌套的复合表达式
            try {
                com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
                String jsonStr = objectMapper.writeValueAsString(operand);
                CompositeRuleExpression nestedExpression = objectMapper.readValue(jsonStr, CompositeRuleExpression.class);
                return evaluateCompositeExpression(nestedExpression, context);
            } catch (Exception e) {
                log.error("嵌套复合表达式评估失败: operand={}, error={}", operand, e.getMessage());
                return false;
            }
        }
        
        return convertToBoolean(operand);
    }
    
    /**
     * 复合规则表达式
     */
    public static class CompositeRuleExpression {
        private String operator;
        private java.util.List<Object> operands;
        
        public String getOperator() {
            return operator;
        }
        
        public void setOperator(String operator) {
            this.operator = operator;
        }
        
        public java.util.List<Object> getOperands() {
            return operands;
        }
        
        public void setOperands(java.util.List<Object> operands) {
            this.operands = operands;
        }
    }

    /**
     * 创建SpEL评估上下文
     */
    private EvaluationContext createSpelContext(Map<String, Object> context) {
        StandardEvaluationContext evalContext = new StandardEvaluationContext();
        
        if (context != null) {
            context.forEach(evalContext::setVariable);
        }
        
        // 添加常用变量
        evalContext.setVariable("now", System.currentTimeMillis());
        evalContext.setVariable("today", java.time.LocalDate.now());
        evalContext.setVariable("time", java.time.LocalTime.now());
        
        return evalContext;
    }

    /**
     * 将结果转换为布尔值
     */
    private boolean convertToBoolean(Object result) {
        if (result == null) {
            return false;
        }
        
        if (result instanceof Boolean) {
            return (Boolean) result;
        }
        
        if (result instanceof Number) {
            return ((Number) result).doubleValue() != 0;
        }
        
        if (result instanceof String) {
            String str = (String) result;
            return !str.isEmpty() && !"false".equalsIgnoreCase(str) && !"0".equals(str);
        }
        
        return true; // 其他非null对象默认为true
    }

    /**
     * 评估结果
     */
    public static class EvaluationResult {
        private boolean success;
        private boolean result;
        private String message;
        private long duration;

        public static EvaluationResult success(boolean result, long duration) {
            EvaluationResult evalResult = new EvaluationResult();
            evalResult.success = true;
            evalResult.result = result;
            evalResult.duration = duration;
            return evalResult;
        }

        public static EvaluationResult error(String message) {
            return error(message, 0);
        }

        public static EvaluationResult error(String message, long duration) {
            EvaluationResult evalResult = new EvaluationResult();
            evalResult.success = false;
            evalResult.result = false;
            evalResult.message = message;
            evalResult.duration = duration;
            return evalResult;
        }

        // Getters
        public boolean isSuccess() { return success; }
        public boolean getResult() { return result; }
        public String getMessage() { return message; }
        public long getDuration() { return duration; }
    }

    /**
     * 批量评估结果
     */
    public static class BatchEvaluationResult {
        private boolean success = true;
        private boolean result = false;
        private String failureReason;
        private long totalDuration;
        private Map<String, EvaluationResult> results = new java.util.HashMap<>();

        public void addResult(String ruleCode, EvaluationResult result) {
            results.put(ruleCode, result);
        }

        // Getters and Setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        
        public boolean getResult() { return result; }
        public void setResult(boolean result) { this.result = result; }
        
        public String getFailureReason() { return failureReason; }
        public void setFailureReason(String failureReason) { this.failureReason = failureReason; }
        
        public long getTotalDuration() { return totalDuration; }
        public void setTotalDuration(long totalDuration) { this.totalDuration = totalDuration; }
        
        public Map<String, EvaluationResult> getResults() { return results; }
    }
}
